#coding=utf-8
from __future__ import with_statement
from uliweb import expose
from datetime import date, timedelta
from uliweb.orm import get_model
import datetime

def __begin__():
    from uliweb import function
    return function('require_login')()

@expose('/tests/plans')
class TestPlanView(object):
    def __init__(self):
        self.model = get_model('testplan')
        self.change_model = get_model('testchangerequest')

    def _create_query(self, url):
        """
        创建查询View
        """
        from uliweb.utils.generic import QueryView
        from plugs.ui.jquery.uimultiselect.widgets import UIMultiSelectField
        from uliweb.form import UnicodeField, SelectField
        from uliweb.utils.generic import ReferenceSelectField
        from query.forms import QueryForm
        from datetime import timedelta
        
        requirement_title = UnicodeField(label='准开发需求')
        test_title = UnicodeField(label='测试名称或编号')
        tx = ReferenceSelectField('tx', label='条线')
        kfzx = ReferenceSelectField('kfzx', label='开发中心')
        fields = [('requirement_title', requirement_title), ('tx', tx), 
            ('kfzx', kfzx), ('test_title', test_title)]
        layout = [('test_title',), 
            ('requirement_title', 'tx', 'kfzx')]
        query = QueryView(self.model, ok_url=url, fields=fields, layout=layout,
            form_cls=QueryForm)
        return query

    def list(self):
        """
        周报列表
        """
        
        from uliweb.utils.generic import ListView, get_sort_field
        
        Requirement = get_model('requirement')
        
        query = self._create_query(url_for(self.__class__.list))
        c = query.run()
        
        condition = None
        _con = None
        if c.get('requirement_title'):
            _con = Requirement.c.title.like('%'+c['requirement_title']+'%') | Requirement.c.req_id.like('%'+c['requirement_title']+'%')
            _r = Requirement.filter(_con).values('id')
            req_ids = [x[0] for x in _r]
            if req_ids:
                condition = (self.model.c.requirement.in_(req_ids)) & condition
        
        if c.get('test_title'):
            condition = (self.model.c.test_id.like('%'+c['test_title']+'%') | self.model.c.title.like('%'+c['test_title']+'%')) & condition
        if c.get('tx'):
            condition = (self.model.c.tx == int(c['tx'])) & condition
        if c.get('kfzx'):
            condition = (self.model.c.kfzx == int(c['kfzx'])) & condition
            
        pageno = int(request.values.get('page', 1)) - 1
        rows_per_page=int(request.values.get('rows', settings.get_var('PARA/ROWS_PER_PAGE', 10)))

        order_by = get_sort_field(self.model)
        
        def test_id(value, obj):
            return '<a href="/tests/plans/view/%d">%s</a>' % (obj.id, value)
        
        def title(value, obj):
            return '<a href="/tests/plans/view/%d">%s</a>' % (obj.id, value)
        
        fields_convert_map = {'test_id':test_id, 'title':title}
        view = ListView(self.model, condition=condition, fields_convert_map=fields_convert_map, 
            order_by=order_by, rows_per_page=rows_per_page, pageno=pageno)
        if 'data' in request.values:
            return json(view.json())
        elif 'download' in request.values:
            fields_convert_map = {}
            return view.download('test_plans.xls', action='download', timeout=0,
                fields_convert_map=fields_convert_map)
        else:
            result = view.run(head=True, body=False)
            result.update({'query_form':query.form, 'table':view})
            return result

    def _create_testplan_list_view(self, test_id):
        """
        创建测试计划功能点View
        """
        from uliweb.utils.generic import ListView, get_sort_field
        from uliweb.orm import get_model
        
        Task = get_model('tasks_develop')
        F = get_model('sysfunction')
        Functions = get_model('testplan_functions')
        C = get_model('sysfunctioncategory')
        
        order_by = get_sort_field(Functions) or [Functions.c.task, F.c.category, Functions.c.function]
        
        pageno = int(request.values.get('page', 1)) - 1
        rows_per_page=int(request.values.get('rows', settings.get_var('PARA/ROWS_PER_PAGE', 10)))
        
        def category(value, obj):
            if obj.function._category_:
                return obj.function.category.name
            else:
                return ''
            
        def function_code(value, obj):
            return obj.function.function_code
        
        fields_convert_map = {'category':category, 'function_code':function_code}
        
        condition = (Functions.c.testplan == int(test_id)) & (Functions.c.function==F.c.id)
        view = ListView(Functions, condition=condition, pagination=True,
            table_width=False, id='functions_table', fields_convert_map=fields_convert_map,
            order_by=order_by, rows_per_page=rows_per_page, pageno=pageno)
        return view
    
    def testplan_functions(self, test_id):
        """
        测试计划功能点列表
        """
        Functions = get_model('testplan_functions')
        
        view = self._create_testplan_list_view(test_id)
#        if Functions.filter(view.condition).count() == 0:
#            return json({'table':'', 'info':{'total':0}})
        return json(view.json())

    def _get_test_type(self, idn):
        """
        根据计划的idn字段来判断是什么测试类型
        """
        type = '0'
        if idn == '0060':
            type = '3' #SIT
        elif idn == '0070':
            type = '4' #UAT
        elif idn == '0090':  #VT
            type = '5'
        return type
        
    def assign(self):
        """
        请求url中带planexecute=xxx
        
        关联测试计划，如果没有则创建新的测试计划，如果已经存在，则可以让用户选择
        要关联的测试计划
        
        检查是否存在，则根据系统及测试类型（SIT, UAT），并且测试没完成的（有可能
        有以前做过的测试计划）
        """
        from sqlalchemy.sql import and_, or_
        
        PlanExecute = get_model('planexecute')
        
        pe_id = request.GET.get('planexecute', None)
        if pe_id:
            pe = PlanExecute.get(PlanExecute.c.id==int(pe_id))
            if not pe:
                error('找不到对应的计划执行记录 [%s]' % pe_id)
            if not pe.task.version_date:
                error('任务 [<a href="/tasks/develop/%d">%s</a>] 没有填写投产时间' % (pe._task_, pe.task.name))
            
            #检查是否有合适的计划存在
            test_type = self._get_test_type(pe.plan.idn)
            condition = and_(self.model.c.system==pe._system_,
                self.model.c.type==test_type,
                or_(self.model.c.plan_end_date>pe.plan.begin_date,
                    self.model.c.origin_end_date>pe.plan.begin_date),
                self.model.c.status.in_(['1', '2']))
                
            result = []
            for row in self.model.filter(condition).order_by(self.model.c.plan_end_date):
                result.append(row)
            #如果存在，则显示选择现有计划的列表，如果不存在，则直接跳转到新建计划去
            if len(result) > 0:
                return {'plans':result, 'planexecute_id':pe_id}
            else:
                return redirect(url_for(self.__class__.add, planexecute=pe_id))
        else:
            error('计划执行记录不能为空')
        
    def assign_to(self):
        """
        用于处理任务与计划的关联，需要url传入 planexecute=xxx&test_id=yyy
        """
        from uliweb.utils import date
        
        PlanExecute = get_model('planexecute')
        TestPlan_Functions = get_model('testplan_functions')
        
        pe_id = request.GET.get('planexecute')
        test_id = request.GET.get('test_id')
        
        if not pe_id or not test_id:
            error("传入参数不完整！")
            
        #将test_id保存到planexecute中
        pe = PlanExecute.get(int(pe_id))
        pe.testplan = int(test_id)
        pe.save()
        
        #向test_plan添加准开发需求和任务
        plan = self.model.get(int(test_id))
        
        #将任务中的功能点复制到测试计划上去
        #添加之前要先检查是不是已经存在关联过的任务，如果存在则不再重复关联
        if not plan.tasks.has(pe._task_):
            for t in pe.task.task_functions.all():
                d = t.to_dict(fields=['task', 'function', 'change_status', 'open_status',
                    'open_secverify', 'test_point'])
                d['update_date'] = date.now()
                d['update_user'] = request.user.id
                d['testplan'] = plan.id
                d['kfzx']= pe.task._kfzx_
                d['system']= pe.task._task_sys_
                obj = TestPlan_Functions(**d)
                obj.save()
        
        plan.requirements.add(pe.task._requirement_)
        plan.tasks.add(pe._task_)
        
        return redirect('/tasks/develop/%d#plan' % pe._task_)
        
    def add(self):
        """
        添加新测试计划
        """
        
        from uliweb.utils.generic import AddView
        from datetime import date
        from sqlalchemy.sql import func
        from uliweb.utils.common import get_choice, get_var
        
        System = get_model('system')
        TestPlan = get_model('testplan')
        PlanExecute = get_model('planexecute')
        
        today = date.today()
        
        default_data = {}
        data = {}
        pe_id = request.GET.get('planexecute', None)
        if pe_id:
            pe = PlanExecute.get(PlanExecute.c.id==int(pe_id))
            if not pe:
                error('找不到对应的计划执行记录 [%s]' % pe_id)
            if not pe.task.version_date:
                error('任务 [<a href="/tasks/develop/%d">%s</a>] 没有填写投产时间' % (pe._task_, pe.task.name))
                
            default_data['tasks'] = [pe._task_]
            default_data['planexecute'] = int(pe_id)
            default_data['requirements'] = [pe.task._requirement_]
            default_data['tx'] = pe.task._tx_
            default_data['kfzx'] = pe.task._kfzx_
            default_data['system'] = pe._system_
            default_data['version_date'] = pe.task.version_date
            default_data['status'] = '1'
            default_data['approve'] = '0'
            default_data['submitter'] = request.user.id
            default_data['submit_date'] = today
            default_data['create_type'] = '2'   #自动创建
            
            #获得初始开始时间和结束时间
            data['origin_begin_date'] = pe.plan.begin_date
            data['origin_end_date'] = pe.plan.end_date
            
            #获得测试类型
            data['type'] = self._get_test_type(pe.plan.idn)
            
            if pe.task.assignment:
                data['project_name'] = pe.task.assignment.name
            
            meta = 'AddTaskForm'
            
        else:
            default_data['status'] = '1'
            default_data['approve'] = '0'
            default_data['submitter'] = request.user.id
            default_data['submit_date'] = today
            default_data['create_type'] = '1'   #手动创建
            
            meta = 'AddForm'
            
        def pre_save(data):
            year = today.year
            
            v = TestPlan.filter(TestPlan.c.test_id.like('%d-%%' % year)).values_one(func.max(TestPlan.c.test_id))[0]
            if not v:
                data['test_id'] = '%d-%04d' % (year, 1)
            else:
                data['test_id'] = v[:5] + '%04d' % (int(v[5:])+1)
                
            #计算title
            t = []
            if pe_id:
                t.append(pe.system.abbr)
            else:
                sys = System.get(data['system'])
                t.append(sys.abbr)
                data['kfzx'] = sys._kfzx_
                
            if pe_id:
                t.append(u'(%d%02d)' % (pe.task.version_date.year, pe.task.version_date.month))
            else:
                if data.get('version_date'):
                    t.append(u'(%d%d)' % (data['version_date'].year, data['version_date'].month))
            if data.get('keywords'):
                t.append(data['keywords'])
            t.append(u'_%s' % unicode(get_choice(get_var('PARA/TEST_CONTENT'), data.get('content_type')), 'utf-8'))
            t.append(u'_%s' % unicode(get_choice(get_var('PARA/TEST_TYPE'), data.get('type')), 'utf-8'))
            data['title'] = ''.join(t)
            
        def post_created_form(fcls, model):
            from uliweb.utils.common import str_value
            from uliweb.form.layout import TableLayout
            
            query = System.get_systems()
            r = [(str_value(x.kfzx.name), x.id, str_value(unicode(x))) for x in query]
            
            fcls.test_systems.choices = r
            fcls.rel_systems.choices = r
#            fcls.need_systems.choices = r
            
            fcls.layout_class = TableLayout
            
        def get_form_field(name):
            from uliweb.utils.generic import RemoteField, ManyToManySelectField
            from forms import EnvField
            
            if name == 'requirement':
                return RemoteField('准开发需求', name=name, url='/requirements/search')
            if name == 'new_env':
                return EnvField(label='')
            
        def post_save(obj, data):
            from uliweb.utils import date
            TestPlan_Functions = get_model('testplan_functions')
            TestEnv = get_model('testenv')
            #绑定测试计划
            pe.testplan = obj.id
            pe.save()
            
            #保存后，将关联的任务的功能点绑定到测试计划上来
            #将任务中的功能点复制到测试计划上去
            #添加之前要先检查是不是已经存在关联过的任务，如果存在则不再重复关联
            for t in pe.task.task_functions.all():
                d = t.to_dict(fields=['task', 'function', 'change_status', 'open_status',
                    'open_secverify', 'test_point'])
                d['update_date'] = date.now()
                d['update_user'] = request.user.id
                d['testplan'] = obj.id
                d['kfzx']= pe.task._kfzx_
                d['system']= pe.task._task_sys_
                record = TestPlan_Functions(**d)
                record.save()
                
            for row in data['new_env']:
                row['testplan'] = int(obj.id)
                row['system'] = obj._system_
                e = TestEnv(**row)
                e.save()
            return True
        
        view = AddView(self.model, '/tests/plans/view/{id}', 
            data=data, default_data=default_data, meta=meta,
            post_created_form=post_created_form, get_form_field=get_form_field,
            pre_save=pre_save, post_save=post_save)
        return view.run()
    
    def _get_view(self, obj, **kwargs):
        from uliweb.utils.generic import DetailView

        view = DetailView(self.model, obj=obj, **kwargs)
        return view
    
    def view(self, id):
        """
        问题测试计划
        """
        
        obj = self.model.get(int(id))
        TestEnv = get_model('testenv')
        
        e = []
        for row in TestEnv.filter(TestEnv.c.testplan==int(id)):
            d = row.to_dict()
            e.append(d)
             
        def new_env(value, obj):
            from forms import EnvField
            
            f = EnvField('', static=True)
            return f.html(e)
        
        view = self._get_view(obj, fields_convert_map={'new_env':new_env})
        testplan_functions_view = self._create_testplan_list_view(id)
        result = view.run()
        
        result['plan_approve_form'] = self._get_approve_view(obj, '1').form
        result['env_approve_form'] = self._get_approve_view(obj, '2').form
        result['table'] = testplan_functions_view
        return result
    
    def _get_approve_view(self, obj, type):
        """
        获得计划或环境审批View
        """
        
        from uliweb.utils.generic import EditView
        from uliweb.form.layout import TableLayout
        
        data = {}
        if type == '1':
            meta = 'ApprovePlanForm'
            url = url_for(TestPlanView.plan_approve, id=obj.id)
            data['plan_begin_date'] = obj.plan_begin_date or obj.origin_begin_date
            data['plan_end_date'] = obj.plan_end_date or obj.origin_end_date
            data['real_mode'] = obj.real_mode or obj.mode
        else:
            meta = 'ApproveEnvForm'
            url = url_for(TestPlanView.env_approve, id=obj.id)
            
        def get_form_field(name, obj):
            from uliweb.form import RadioSelectField, TextField
            from uliweb import settings
            
            if name == 'appr':
                return RadioSelectField('审批结果：', default='1', choices=settings.get_var('PARA/TEST_APPROVE'))
            elif name == 'reason':
                return TextField('原因：')
            
        def post_created_form(fcls, model, obj):
            fcls.layout_class = TableLayout
            
        def pre_save(obj, data):
            if data['appr'] == '1':
                if type == '1':
                    if obj.approve == '5':
                        data['approve'] = '1'
                    elif obj.approve == '0':
                        data['approve'] = '4'
                else:
                    if obj.approve == '4':
                        data['approve'] = '1'
                    elif obj.approve == '0':
                        data['approve'] = '5'
            else:
                data['approve'] = data['appr']
                
        def post_save(obj, data):
            Comment = get_model('testplancomment')
            obj = Comment(test_task=obj.id, approver=request.user.id, 
                approve_date=datetime.datetime.now(), comment=data['reason'],
                approve=data['appr'], type=type)
            obj.save()
            
        plan_approve_view = EditView(self.model, obj=obj, meta=meta,
            get_form_field=get_form_field, post_created_form=post_created_form,
            form_args={'action':url}, data=data,
            ok_url=url_for(TestPlanView.view, id=obj.id), pre_save=pre_save,
            post_save=post_save)

        return plan_approve_view
    
    def edit(self, id):
        """
        编辑测试计划
        """
        
        from uliweb.utils.generic import EditView
        
        System = get_model('system')
        Task = get_model('tasks_develop')
        Plan = get_model('planexecute')
        TestPlan_Functions = get_model('testplan_functions')
        TestEnv = get_model('testenv')
        
        obj = self.model.get(int(id))

        def pre_save(obj, data):
            #如果审批拒绝，修改后，审批结果将改为待审批
            if obj.approve == '3':
                data['approve'] = '0'
                
            #计算title
            t = []
            t.append(obj.system.abbr)
            t.append(u'(%d%d)' % (obj.version_date.year, obj.version_date.month))
            if data['keywords']:
                t.append(data['keywords'])
            t.append(u'_%s' % obj.get_display_value('content_type', data['content_type']))
            t.append(u'_%s' % obj.get_display_value('type', data['type']))
            data['title'] = ''.join(t)
            
            #处理当任务计划变化时，准开发需求和任务的变化
            old_tasks = set(obj.tasks.ids())
            old_planexecutes = set(obj.testplan_taskexecutes.ids())
            tasks = set()
            reqs = set()
            for x in data['planexecutes']:
                p = Plan.get(x)
                tasks.add(p._task_)
                reqs.add(p.task._requirement_)
                
            data['tasks'] = list(tasks)
            data['requirements'] = list(reqs)
            
            #如果任务发生变化，则删除任务对应的测试功能点
            for x in old_tasks ^ tasks:
                TestPlan_Functions.filter(TestPlan_Functions.c.task==x).remove()
                
            #清除被删除的planexecute与testplan之间的关系
            planexecutes_diff = list(old_planexecutes^set(data['planexecutes']))
            if planexecutes_diff:
                Plan.filter(Plan.c.id.in_(planexecutes_diff)).update(testplan=None)
               
        def post_save(obj, data):
            TestEnv.filter(TestEnv.c.testplan == int(id)).remove()
            for row in data['new_env']:
                row['testplan'] = int(id)
                row['system'] = obj._system_
                e = TestEnv(**row)
                e.save()
            return True
                
        def post_created_form(fcls, model, obj):
            from uliweb.utils.common import str_value
            from uliweb.form.layout import TableLayout
            
            query = System.get_systems()
            r = [(str_value(x.kfzx.name), x.id, str_value(unicode(x))) for x in query]
            
            fcls.test_systems.choices = r
            fcls.rel_systems.choices = r
#            fcls.planexecutes.query = obj.planexecutes.all()
#            fcls.need_systems.choices = r
            
            fcls.layout_class = TableLayout

        if obj.create_type != '1':
            meta = 'EditTaskForm'
        else:
            meta = 'EditForm'
            
        def get_form_field(name, obj):
            from uliweb.utils.generic import RemoteField, ManyToManySelectField
            from forms import EnvField
            
            if name == 'requirement':
                if obj._requirement_:
                    alt = obj.requirement.title
                else:
                    alt = ''
                return RemoteField('准开发需求', name=name, required=True, 
                    url='/requirements/search', alt=alt)
            elif name == 'planexecutes':
                return ManyToManySelectField('planexecute', label='任务计划', query=obj.testplan_taskexecutes.all())
            elif name == 'new_env':
                return EnvField(label='')
            
        e = []
        for row in TestEnv.filter(TestEnv.c.testplan==int(id)):
            d = row.to_dict()
            e.append(d)
                
        data = {'planexecutes':obj.testplan_taskexecutes.ids(), 'new_env':e}
        view = EditView(self.model, url_for(TestPlanView.view, id=id), 
            obj=obj, get_form_field=get_form_field,
            data=data, post_save=post_save,
            post_created_form=post_created_form, pre_save=pre_save,
            meta=meta,
            )
        return view.run()
    
    def delete(self, id):
        """
        删除测试计划
        """
        
        from uliweb.utils.generic import DeleteView
        Comment = get_model('testplancomment')
        PE = get_model('planexecute')
        TestChangeRequest = get_model('testchangerequest')
        obj = self.model.get(int(id))
        
        def pre_delete(obj):
            obj.requirements.clear()
            obj.tasks.clear()
            obj.test_systems.clear()
            obj.rel_systems.clear()
            pe_ids = obj.testplan_taskexecutes.ids()
            PE.filter(PE.c.id.in_(pe_ids)).update(testplan=None)
            obj.testplan_change_requests.all().remove()
            obj.testplan_functions.all().remove()
            obj.comments.all().remove()
            obj.testplan_envs.remove()
            
        view = DeleteView(self.model, url_for(TestPlanView.list), obj=obj,
            pre_delete=pre_delete)
        return view.run()
    
    def plan_approve(self, id):
        """
        计划审批
        """
        
        obj = self.model.get(int(id))
        
        v = self._get_view(obj)
        result = v.run()
        
        view = self._get_approve_view(obj, '1')
        result['plan_approve_form'] = view.form
        result['env_approve_form'] = self._get_approve_view(obj, '2').form
        view.template_data.update(result)
        
        response.template = 'TestPlanView/view.html'
        return view.run()
        
    def env_approve(self, id):
        """
        环境审批
        """
        obj = self.model.get(int(id))
        
        v = self._get_view(obj)
        result = v.run()

        view = self._get_approve_view(obj, '2')
        result['plan_approve_form'] = self._get_approve_view(obj, '1').form
        result['env_approve_form'] = view.form
        view.template_data.update(result)
        
        response.template = 'TestPlanView/view.html'
        return view.run()

    def comments(self, id):
        """
        返回审批记录
        """
        
        from uliweb.utils.generic import make_view_field
        Comment = get_model('testplancomment')
        
        s = []
        obj = self.model.get(int(id))
        for i, row in enumerate(obj.comments):
            d = {'index':i+1, 'approver':row.get_display_value('approver'),
                'approve_date':row.get_display_value('approve_date'),
                'comment':make_view_field(Comment.comment, row)['display'], 
                'approve':row.get_display_value('approve'),
                'type':row.get_display_value('type')}
            s.append(d)
        return json(s)
    
    def execute(self, id):
        """
        执行测试计划
        """
        
        from uliweb.utils.generic import EditView
        
        def post_created_form(fcls, model, obj):
            from uliweb.form.layout import TableLayout

            fcls.layout_class = TableLayout
            
        def pre_save(obj, data):
            #如果填写了开始时间，但是测试执行状态为未开始，则改为进行中
            if data.get('real_begin_date') and data.get('status') == '1':
                data['status'] = '2'
            #如果填写了完成时间，但是测试执行状态为进行中，则改为完成
            if data.get('real_end_date') and data.get('status') == '2':
                data['status'] = 'F'
            
        def post_save(obj, data):
            obj.planexecute.finish_begin_date = data.get('real_begin_date')
            obj.planexecute.finish_end_date = data.get('real_end_date')
            
            obj.planexecute.save()
        
        obj = self.model.get(int(id))
        view = EditView(self.model, url_for(TestPlanView.view, id=id), 
            obj=obj, meta='ExecuteForm', post_created_form=post_created_form,
            post_save=post_save, pre_save=pre_save)
        return view.run()
    
    def change(self, id):
        """
        提交计划变更
        """
        
        from uliweb.utils.generic import EditView
        
        obj = self.model.get(int(id))
        change_model = self.change_model
        
        class ChangeView(EditView):
            success_msg = '请求已经提交'
            
            def save(self, obj, data):
                old_value = obj.to_dict(manytomany=True, convert=False)
                if data != old_value:
                    c = change_model(test_task=obj.id, old_value=old_value,
                        new_value=data, submitter=request.user.id,
                        submit_date=datetime.date.today(),
                        approve='0')
                    c.save()
                    return True
                else:
                    return False
        
        System = get_model('system')
        
        def post_created_form(fcls, model, obj):
            from uliweb.utils.common import str_value
            from uliweb.form.layout import TableLayout
            
            query = System.get_systems()
            r = [(str_value(x.kfzx.name), x.id, str_value(unicode(x))) for x in query]
            
            fcls.test_systems.choices = r
            fcls.rel_systems.choices = r
            fcls.need_systems.choices = r
            
            fcls.layout_class = TableLayout

        view = ChangeView(self.model, url_for(TestPlanView.view, id=obj.id), 
            obj=obj, post_created_form=post_created_form, 
            meta='ChangeForm',
            )
        return view.run()
    
    def _create_changes_query(self, url):
        """
        创建查询View
        """
        from uliweb.utils.generic import QueryView
        from uliweb import settings
        from query.forms import QueryForm
        from uliweb.form import SelectField
        from plugs.ui.jquery.uimultiselect.widgets import UIMultiSelectField
        
        approve = UIMultiSelectField(label='审批结果', choices=settings.get_var('PARA/TEST_APPROVE_STATUS'))
        closed = SelectField('显示已审批', choices=settings.get_var('PARA/YES_NO'), default='0')
        fields = [('approve', approve), ('closed', closed)]
        layout = [('approve', 'closed')]
        query = QueryView(self.change_model, ok_url=url, fields=fields, layout=layout,
            form_cls=QueryForm)
        return query

    def changes(self):
        """
        计划变更列表
        """
        
        from uliweb.utils.generic import ListView, get_sort_field
        
        query = self._create_changes_query(url_for(TestPlanView.changes))
        c = query.run()
        
        condition = None
        _con = None
        
        if c.get('closed') == '0':
            condition = (self.change_model.c.approve == '0') & condition
        if c.get('approve'):
            condition = (self.change_model.c.approve.in_(c['approve'])) & condition
            
        pageno = int(request.values.get('page', 1)) - 1
        rows_per_page=int(request.values.get('rows', settings.get_var('PARA/ROWS_PER_PAGE', 10)))

        order_by = get_sort_field(self.change_model) or []
        order_by.insert(0, self.change_model.c.submit_date)
        
        def test_task(value, obj):
            return '<a href="/tests/plans/change_approve_view/%d">%s</a>' % (obj.id, obj.test_task.title)
        
        fields_convert_map = {'test_task':test_task}
        view = ListView(self.change_model, condition=condition, fields_convert_map=fields_convert_map, 
            order_by=order_by, rows_per_page=rows_per_page, pageno=pageno)
        if 'data' in request.values:
            return json(view.json())
        elif 'download' in request.values:
            fields_convert_map = {}
            return view.download('test_changes_req.xls', action='download', timeout=0,
                fields_convert_map=fields_convert_map)
        else:
            result = view.run(head=True, body=False)
            result.update({'query_form':query.form, 'table':view})
            return result

    def change_approve_view(self, id):
        """
        计划变更审批显示
        """
        def _make_diff_table(od, nd):
            from uliweb.utils.generic import make_view_field
            
            s = []
            for k, v in od.iteritems():
                s.append('<tr><th align="right">')
                field = self.model.properties[k]
                s.append(field.verbose_name or field.property_name)
                s.append('</th><td>')
                s.append(make_view_field(field, plan, value=v)['display'])
                s.append('</td><td>')
                s.append(make_view_field(field, plan, value=nd[k])['display'])
                s.append('</td></tr>')
            return ''.join(s)

        change = self.change_model.get(int(id))
        plan = change.test_task
        
        old_data, new_data= self._diff_value(change.old_value, change.new_value)
        plan.update(**new_data)
        view = self._get_view(plan, template_data={'change':change, 'diff_data':_make_diff_table(old_data, new_data)})
        result = view.run()
        
        return result
        
    def change_approve(self, change_id):
        """
        计划变更审批处理
        """
        obj = self.change_model.get(int(change_id))
        if request.GET.get('action') == 'yes':
            obj.approve = '1'
        else:
            obj.approve = '3'
        obj.approver = request.user
        obj.approve_date = date.today()
        ret = obj.save()
        
        plan = obj.test_task
        plan.update(**obj.new_value)
        plan.save()
        
        Comment = get_model('testplancomment')
        c = Comment(type='3', test_task=plan.id, approver=request.user, 
            approve_date=date.today(), comment='', approve=obj.approve)
        c.save()
        
        flash('变更审批成功！')
        return redirect(url_for(TestPlanView.view, id=plan.id))
    
    def _diff_value(self, old_value, new_value):
        """
        比较两个字典之间的差异，以new_value为基准
        """
        from uliweb.utils.common import str_value
        
        nd, od = {}, {}
        for k, v in new_value.iteritems():
            if k not in old_value or str_value(old_value[k])!=str_value(new_value[k]):
                od[k] = old_value.get(k, '')
                nd[k] = new_value[k]
        return od, nd
    
#    @expose('version_verification_test_import')
    def version_verification_test_import(self):
        from uliweb.utils.generic import AddView
        from uliweb.orm import get_model,and_
        from uliweb.utils.common import log
        from decimal import Decimal
        import datetime
        
        class ImportView(AddView):
            def on_success(self, data,aaa):
                from parse_excel import open_book
                try:
                    Versionverificationtest = get_model('versionverificationtest')
                    System = get_model('system')
                    Tasks_develop = get_model('tasks_develop')
                    Plan = get_model('plan')
                    Planedit = get_model('planedit')
                    Planexecute = get_model('planexecute')
                    
                    #取投产日浮动周期范围
                    float_days = settings.get_var('PARA/VERSION_DATE_FLOAT')
                    version_date_float_days= datetime.timedelta(days=float_days)
                    
                    fields = ['system', 'sys_abbr', 'begin_date', 'end_date',
                        'version_date', ('mode', 'PARA/TEST_MODE'),
                        ]
                    f = {}  #字段列表信息
                    c = {}  #保存要转换值的字段信息，key为字段名，value为对应settings中的选项
                    def get_key(key, value):
                        """
                        用于反向根据值来获取key，如[('1', '高'), ('2', '低')]，根据'高'得到'1'
                        """
                        from uliweb import settings
                        
                        s = settings.get_var(key, [])
                        for k, v in s:
                            if isinstance(value, unicode):
                                value = value.encode('utf-8')
                            if isinstance(v, unicode):
                                v = value.encode('utf-8')
                                
                            if value == v:
                                return k
                        
                    for i, item in enumerate(fields):
                        if isinstance(item, (tuple, list)):
                            c[item[0]] = item[1]
                            f[i] = item[0]
                        else:
                            f[i] = item
                    last_sys = None
                    i = 0
                    for line in open_book(file_contents=data['func_file']['file'].read(), 
                        log=log, begin=1, fields=f):
                        i += 1
                        d = line.copy()
                        #process value convert
                        for k, v in c.iteritems():
                            if k in d:
                                d_v = d[k]
                                d[k] = get_key(v, d_v)
                                
#                        if isinstance(d['daily_visit'],int) or isinstance(d['daily_visit'],float) or isinstance(d['daily_visit'],Decimal):
#                            pass
#                        elif not d['daily_visit'].strip():
#                            d['daily_visit']=0
                        
                        sys_id = d.pop('system')
                        if sys_id:
                            s = System.get(System.c.sys_id==sys_id)
                            last_sys = s
                        if not last_sys:
                            flash('第%d行系统简称查询不到，请检查是否没有填写或大小写不一致' % i)
                            return redirect(url_for(SysFunctionView.list, id=id))
                        else:
                            d['system'] = last_sys.id
                            p={}
                            p['begin_date']=d['begin_date']
                            p['end_date']=d['end_date']
                            
                            row = Versionverificationtest.get(and_(Versionverificationtest.c.system==d['system'],Versionverificationtest.c.version_date==d['version_date']))
                            if row:
                                row.update(**d)
                            else:
                                row = Versionverificationtest(**d)
                            row.save()
                            #创建或更新已创建的任务版本验证测试计划
                            task_dev = Tasks_develop.filter(and_(Tasks_develop.c.task_sys == d['system'],
                                                                Tasks_develop.c.version_date >= d['version_date']-version_date_float_days,
                                                                Tasks_develop.c.version_date <= d['version_date']+version_date_float_days))
                            for row_task in task_dev:
                                #处理plan表
                                plan_obj=Plan.get(and_(Plan.c.task==row_task.id,Plan.c.idn=='0090'))
                                if plan_obj:
                                   plan_obj.update(**p)
                                else:
                                    plan_obj = Plan(task=row_task.id, title=u'版本验证测试', type='1', 
                                                    end_date=d['end_date'], begin_date=d['begin_date'],
                                                    test_type=True,
                                                    idn='0090', main_sys=d['system'], is_public=False,
                                                    is_reserved=False,
                                                    stage='10', weight=0,
                                                    )
                                plan_obj.save()
                                plan_obj.systems.add(d['system'])
                                #处理planedit表
                                planedit_obj = Planedit.get(and_(Planedit.c.task==row_task.id,Plan.c.idn=='0090'))
                                if planedit_obj:
                                    planedit_obj.update(**p)
                                else:
                                    planedit_obj = Planedit(task=row_task.id, title=u'版本验证测试', type='1', 
                                                    end_date=d['end_date'], begin_date=d['begin_date'],
                                                    test_type=True,
                                                    idn='0090', main_sys=d['system'], is_public=False,
                                                    is_reserved=False,
                                                    stage='10', weight=0,
                                                    )
                                planedit_obj.save()
                                planedit_obj.systems.add(d['system'])
                                #处理planexecute表
                                planexecute_obj=Planexecute.get(and_(Planexecute.c.task==row_task.id,
                                                                    Planexecute.c.plan==plan_obj.id,
                                                                    Planexecute.c.system==d['system']))
                                if planexecute_obj:
                                    planexecute_obj.update(**p)
                                else:
                                    planexecute_obj=Planexecute(task=row_task.id, plan=plan_obj.id, type='1', system=d['system']) 
                                
                                planexecute_obj.save()  
                            
                    flash('导入成功，共导入%d条记录' % i)
                    return redirect(url_for(TestPlanView.list))
                except Exception, e:
                    log.exception(e)
                    flash('处理过程有问题，请与程序员联系')
                    return redirect(url_for(TestPlanView.list))
        
        
        from forms import VerificationTestImportForm
        if request.user.department in ('3'):
            view = ImportView('versionverificationtest', url_for(TestPlanView.list), fields=(), 
    #                post_created_form=post_created_form, 
                form_cls=VerificationTestImportForm)
            return view.run()
        else:
            flash('你不是测试人员，无法导入版本验证测试计划！')
            return redirect(url_for(TestPlanView.list))
    
    
def post_save(sender, instance, created, data, old_data):
    """
    处理任务功能点的修改 Task_Functions
    """
    from uliweb.orm import get_model
    from uliweb.utils import date
    from uliweb import request
    
    tablename = sender.tablename
    if tablename != 'task_functions':
        return

    TF = get_model('testplan_functions')
    PE = get_model('planexecute')
    d = {}
    fields = ['change_status', 'open_status', 'open_secverify', 'test_point']
    for f in fields:
        if f in data:
            d[f] = data[f]
    if d:
        query = TF.filter(TF.c.task==instance._task_).filter(TF.c.function==instance._function_)
        if query.count() > 0:
            query.update(**d)
        else:
            #说明要创建新的记录，则根据当前任务已经关联的测试计划自动创建
            for pe in PE.filter(PE.c.testplan!=None):
                d = {}
                for x in ['task', 'function', 'change_status', 'open_status',
                                    'open_secverify', 'test_point']:
                    d[x] = data[x]
                
                d['update_date'] = date.now()
                d['update_user'] = request.user.id
                d['testplan'] = pe._testplan_
                d['kfzx']= pe.task._kfzx_
                d['system']= pe.task._task_sys_
                obj = TF(**d)
                obj.save()
    
def pre_delete(sender, instance):
    """
    处理任务功能点的删除 Task_Functions
    """
    from uliweb.orm import get_model
    
    tablename = sender.tablename
    if tablename != 'task_functions':
        return
    
    TF = get_model('testplan_functions')
    TF.filter(TF.c.task==instance._task_).filter(TF.c.function==instance._function_).remove()

